﻿using Apewer.Internals;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Apewer.Network
{

    /// <summary>UDP 客户端。</summary>
    public static class UdpClient
    {

        /// <summary>向所有服务端广播数据。</summary>
        /// <param name="bytes">数据。</param>
        /// <param name="port">服务端端口。</param>
        public static bool Broadcast(byte[] bytes, int port)
        {
            if (bytes == null) return false;
            if (bytes.LongLength == 0) return false;
            if (port < 0) port = 0;
            if (port > 65535) port = 65535;
            try
            {
                var uc = new System.Net.Sockets.UdpClient();
                var ep = new IPEndPoint(IPAddress.Parse("255.255.255.255"), port);
                uc.EnableBroadcast = true;
                uc.Send(bytes, bytes.Length, ep);
                uc.Close();
                return true;
            }
            catch { return false; }
        }

        /// <summary>向指定服务端发送数据。</summary>
        /// <param name="ip">服务端 IP 地址。</param>
        /// <param name="port">服务端端口。</param>
        /// <param name="bytes">数据。</param>
        /// <param name="await">阻塞当前线程，等待服务端返回数据，指定 FALSE 将立刻返回 NULL 值。</param>
        /// <returns>服务器返回的数据。当通过 await 参数指定 FALSE 时，立刻返回 NULL 值。</returns>
        public static byte[] Send(string ip, int port, byte[] bytes, bool await = false)
        {
            if (bytes == null) return null;
            if (bytes.LongLength == 0) return null;
            if (port < 0) port = 0;
            if (port > 65535) port = 65535;
            try
            {
                var uc = new System.Net.Sockets.UdpClient();
                var ep = new IPEndPoint(IPAddress.Parse(ip), port);

                uc.EnableBroadcast = false;
                uc.Send(bytes, bytes.Length, ep);

                var received = null as byte[];
                if (await)
                {
                    var rep = null as IPEndPoint;
                    received = await ? uc.Receive(ref rep) : null;
                }
                uc.Close();
                return received;
            }
            catch { return null; }
        }

        /// <summary>向指定服务端发送数据。</summary>
        /// <param name="ip">服务端 IP 地址。</param>
        /// <param name="port">服务端端口。</param>
        /// <param name="bytes">数据。</param>
        /// <param name="await">阻塞当前线程，等待服务端返回数据，指定 FALSE 将立刻返回 NULL 值。</param>
        /// <returns>服务器返回的数据。当通过 await 参数指定 FALSE 时，立刻返回 NULL 值。</returns>
        public static byte[] Send(byte[] bytes, int port, string ip = "127.0.0.1", bool await = false)
        {
            return Send(ip, port, bytes, false);
        }

        /// <summary>向指定服务端发送数据。</summary>
        /// <param name="ip">服务端 IP 地址。</param>
        /// <param name="port">服务端端口。</param>
        /// <param name="bytes">数据。</param>
        /// <param name="await">阻塞当前线程，等待服务端返回数据，指定 FALSE 将立刻返回 NULL 值。</param>
        /// <returns>服务器返回的数据。当通过 await 参数指定 FALSE 时，立刻返回 NULL 值。</returns>
        public static byte[] Send(int port, byte[] bytes, string ip = "127.0.0.1", bool await = false)
        {
            return Send(ip, port, bytes, false);
        }

        /// <summary>唤醒局域网中拥有指定 MAC 地址的设备。</summary>
        /// <param name="mac">被唤醒设备的 MAC 地址，必须是长度为 6 的字节数组。</param>
        public static void WakeOnLan(byte[] mac)
        {
            if (mac.Length != 6) return;

            var uc = new System.Net.Sockets.UdpClient();
            uc.Connect(IPAddress.Broadcast, 65535);

            var pack = new List<byte>();

            // 前 6 字节为 0xFF。
            for (int i = 0; i < 6; i++) pack.Add(255);

            // 目标 MAC 地址重复 16 次。
            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 6; j++) pack.Add(mac[j]);
            }

            // 发送 102 字节数据。
            uc.Send(pack.ToArray(), pack.Count);

            uc.Close();
        }

    }

}
